package com.fqgj.sentry.filter;

import com.fqgj.sentry.common.exception.BizException;
import com.fqgj.sentry.common.utils.HttpHelper;
import com.fqgj.sentry.common.utils.SortCollection;
import com.fqgj.sentry.exception.ErrorCodeEnums;
import com.fqgj.sentry.manage.entity.McMerchantEntity;
import com.fqgj.sentry.manage.service.ApplicationService;
import com.google.gson.Gson;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.encoding.Md5PasswordEncoder;
import org.springframework.util.NumberUtils;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by liupengpeng on 2017/7/20.
 */
public class SignFilter implements Filter {
    @Autowired
    private ApplicationService applicationService;
    private Gson gson = new Gson();

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        // 防止流读取一次后就没有了, 所以需要将流继续写出去
        ServletRequest requestWrapper = new HttpServletRequestBodyWrapper(httpServletRequest);
        // sign(httpServletRequest, requestWrapper);
        filterChain.doFilter(requestWrapper, servletResponse);

    }

    @Override
    public void destroy() {

    }

    private void sign(HttpServletRequest request, ServletRequest requestWrapper) {

        Map<String, Object> boby = new HashMap<String, Object>();

        String httpMethod = request.getMethod();
        String url = request.getRequestURI();
        boby.put("httpMethod", httpMethod);
        boby.put("requestURI", url);

        String clientSign;
        Long timestamp;


        if ("POST".equalsIgnoreCase(httpMethod)) {
            try {
                String requestBody = HttpHelper.getBodyString(requestWrapper);
                if (!StringUtils.isEmpty(requestBody)) {
                    Map<String, Object> bodyMap = gson.fromJson(requestBody, Map.class);
                    boby.putAll(bodyMap);
                }
                //1、客户端传入的用户身份
                String appkey = (String) boby.get("apikey");
                //2、客户端生成的消息摘要
                clientSign = (String) boby.get("sign");
                //3、客户端时间戳(Unix时间戳)
                String requestTimestamp = boby.get("timestamp").toString();
                try {
                    timestamp = NumberUtils.parseNumber(requestTimestamp, Long.class);
                } catch (Exception e) {
                    throw new BizException(ErrorCodeEnums.sign_error, "This timestamp is not unix timestamp");
                }
                Date clientTime = new Date(timestamp * 1000);
                Date endTime = DateUtils.addMinutes(clientTime, 10);
                Date currentTime = new Date();
                //用户发起请求时的unix时间戳，本次请求签名的有效时间为该时间戳+10分钟。
                if (endTime.before(currentTime)) {
                    throw new BizException(ErrorCodeEnums.sign_error, "this request is timeout");
                }
                boby.remove("sign");
                boby.remove("timestamp");
                boby.put("timestamp", Long.toString(timestamp));
                //根据 apikey  查询  secret
                McMerchantEntity merchantEntity = applicationService.selectMerchantByMerchantCode(appkey);
                if (merchantEntity == null) {
                    throw new BizException(ErrorCodeEnums.merchant_not_exists_error);
                }
                String sourceStr = SortCollection.sort(merchantEntity.getVerifyKey(), boby);
                Md5PasswordEncoder encoder = new Md5PasswordEncoder();
                String serviceSign = encoder.encodePassword(sourceStr, "");
                if (!serviceSign.equals(clientSign)) {
                    throw new BizException(ErrorCodeEnums.sign_error);
                }
            } catch (Exception e) {
                throw new BizException(ErrorCodeEnums.sign_error);
            }
        } else {
            throw new BizException(ErrorCodeEnums.request_service_error);
        }


    }
}
